package com.jackson.springencdec.utils;

/*
 * Author: JacksonCoder
 * FileName: RSAUtils
 * Version: 1.0.0
 * Date: 2025-10-14
 */

//import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;

public class RSAUtils {

    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";

    /* ======  ThreadLocal 复用 Cipher，原签名不动  ====== */
    private static final ThreadLocal<Cipher> ENCRYPT_CIPHER = ThreadLocal.withInitial(() -> {
        try { return Cipher.getInstance(KEY_ALGORITHM); }
        catch (Exception e) { throw new RuntimeException(e); }
    });
    private static final ThreadLocal<Cipher> DECRYPT_CIPHER = ThreadLocal.withInitial(() -> {
        try { return Cipher.getInstance(KEY_ALGORITHM); }
        catch (Exception e) { throw new RuntimeException(e); }
    });

    /* ==================== 以下为原签名，实现被重写 ==================== */

    public static byte[] decryptBASE64(String key) {
        if (key == null) {
            System.out.println(key + "空指针异常发生了！");
        } else {
            System.out.println(key + "未发生异常！");
        }
        return Base64.getDecoder().decode(key);
    }

    public static String encryptBASE64(byte[] bytes) {
        return Base64.getEncoder().encodeToString(bytes);
    }

    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = decryptBASE64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);
        return encryptBASE64(signature.sign());
    }

    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        byte[] keyBytes = decryptBASE64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        return signature.verify(decryptBASE64(sign));
    }

    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
        byte[] keyBytes = decryptBASE64(key);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = DECRYPT_CIPHER.get();
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return rsaSplit(cipher, Cipher.DECRYPT_MODE, data, 128); // 128 分段
    }

    public static byte[] decryptByPrivateKey(String data, String key) throws Exception {
        return decryptByPrivateKey(decryptBASE64(data), key);
    }

    public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
        byte[] keyBytes = decryptBASE64(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = DECRYPT_CIPHER.get();
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return rsaSplit(cipher, Cipher.DECRYPT_MODE, data, 128);
    }

    public static byte[] encryptByPublicKey(String data, String key) throws Exception {
        byte[] keyBytes = decryptBASE64(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = ENCRYPT_CIPHER.get();
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return rsaSplit(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), 117); // 117 分段
    }

    public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
        byte[] keyBytes = decryptBASE64(key);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = ENCRYPT_CIPHER.get();
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return rsaSplit(cipher, Cipher.ENCRYPT_MODE, data, 117);
    }

    public static String getPrivateKey(Map<String, Key> keyMap) throws Exception {
        Key key = keyMap.get(PRIVATE_KEY);
        return encryptBASE64(key.getEncoded());
    }

    public static String getPublicKey(Map<String, Key> keyMap) throws Exception {
        Key key = keyMap.get(PUBLIC_KEY);
        return encryptBASE64(key.getEncoded());
    }

    public static Map<String, Key> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        Map<String, Key> keyMap = new HashMap<>(2);
        keyMap.put(PUBLIC_KEY, keyPair.getPublic());
        keyMap.put(PRIVATE_KEY, keyPair.getPrivate());
        return keyMap;
    }

    /* -------------- 新增：分段加解密工具 -------------- */
    private static byte[] rsaSplit(Cipher cipher, int mode, byte[] data, int maxBlock) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int off = 0, len = data.length;
        while (off < len) {
            int block = Math.min(len - off, maxBlock);
            out.write(cipher.doFinal(data, off, block));
            off += block;
        }
        out.close();
        return out.toByteArray();
    }

    /* -------------- 原 main 方法，仅打印 Base64 字符串 -------------- */
    public static void main(String[] args) throws Exception {
        byte[] encryptData = encryptByPublicKey("123456789",
                "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpp8A8/67GMcXQuOwUEbZySxQZK8YCSY4/GA5JBbuknRahCmAW07GEifxkOLWEo8NGwGxafSmaQwt6QWIhou2cEC9/VHd8Au8bn88keBeJlukRiEBuYwdOirJsS3S9m1XMPZOed4u/9zo4VQwnjqqPnbnNMxWxBwJTNr1rPdSkFQIDAQAB");
        String encryptStr = encryptBASE64(encryptData);
        System.out.println(encryptStr);

        System.out.println("密文 Base64：" + encryptStr);

        // 解密
        String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKmnwDz/rsYxxdC47BQRtnJLFBkrxgJJjj8YDkkFu6SdFqEKYBbTsYSJ/GQ4tYSjw0bAbFp9KZpDC3pBYiGi7ZwQL39Ud3wC7xufzyR4F4mW6RGIQG5jB06KsmxLdL2bVcw9k553i7/3OjhVDCeOqo+duc0zFbEHAlM2vWs91KQVAgMBAAECgYAK+okNKXqU5smnTSAx2EeXM9aUHmoM4derq5HZFd7jsyqwtywqrf5R1mAX2Dee4r4TSQZ6AJl9mxMWET8ZcCKyGHT+Vjuk5ivCZ4R1EF0vgdFoz3Zmmr6M+f7TYSBf42NuhDYYmnUaISt/lXjS3HvJZt3GZ2teu6VuawTe32XegQJBAPDEkWlQx/7kFjxrh+3eIuGtKL26vcIpZTs8l6qT6FNuaRIE3S5/Y2We7v6KcSPcwgrQgPShXyCE26kk7QR5U3UCQQC0Y3Tv68fi7J+o4BsC73bTHmoJSYD+PT3YngKB3dBn4IMAyJgVk4ogY0jL42ub1wul6j9QhD8gXlDVSiX0+xohAkAi609vp9CpzijPcIURYbNLadLL2aal310YP0x75+G9H/lLLLtrf/xJg8iD0E5idiUKbn0VfcYPMFWvbXSWvoX1AkBQtp/AKnPJ6a6W1Y/AhH1agaVYencpSyW9IxZkHWf+0spLM3Y5UUbgrwwHjr67gEFJ/8JMuJ+2T/1DMmZBc4mBAkEA3HAy+GtSU2zz1oDnUN6FkVadgDOhxV3zU/wu45Ss3x3OiWZTimwLR+zFw0nAlDSTfXr0hssrZLKN+I0BfrPgYg==";
        // 用私钥把刚才的密文解密
        byte[] decryptBytes = decryptByPrivateKey(decryptBASE64(encryptStr), privateKey);
        String plainResult = new String(decryptBytes, "UTF-8");

        System.out.println("解密结果：" + plainResult);

    }

    /* -------------- 下面两个简易 Base64 工具继续保留 -------------- */
    public static String encryptAndDecode(String inStr) {
        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) a[i] = (char) (a[i] ^ 't');
        return new String(a);
    }

    public static String encrypt(String str) {
        try { return Base64.getEncoder().encodeToString(str.getBytes("UTF-8")); }
        catch (Exception e) { e.printStackTrace(); return null; }
    }

    public static String decode(String str) {
        try { return new String(Base64.getDecoder().decode(str), "UTF-8"); }
        catch (Exception e) { e.printStackTrace(); return null; }
    }
}
